﻿namespace ManagedChangeJournal.Native.Win32
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;

    /// <summary>
    /// 
    /// </summary>
    internal static class Win32DeviceControl
    {
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="Structure"></typeparam>
        /// <param name="handle"></param>
        /// <param name="code"></param>
        /// <param name="structure"></param>
        /// <param name="bufferlen"></param>
        /// <returns></returns>
        internal static byte[] ControlWithInput<Structure>(
            IntPtr handle, Win32ControlCode code,
            ref Structure structure, int bufferlen)
            where Structure : struct
        {
            uint datalen;
            byte[] buffer;
            bool controlResult;       
            GCHandle structureHandle, bufferHandle;
            IntPtr structurePointer, bufferPointer;

            buffer = new byte[bufferlen];
            bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            structureHandle = GCHandle.Alloc(structure, GCHandleType.Pinned);
            bufferPointer = bufferHandle.AddrOfPinnedObject();
            structurePointer = structureHandle.AddrOfPinnedObject();

            try
            {
                controlResult =
                    Win32Methods.DeviceIoControl(handle, (uint)code,
                    structurePointer, (uint)Marshal.SizeOf(structure),
                    bufferPointer, (uint)buffer.Length,
                    out datalen, IntPtr.Zero);
            }
            catch
            {
                throw;
            }
            finally
            {
                structureHandle.Free();
                bufferHandle.Free();
            }

            if (!controlResult)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            if (datalen < bufferlen && datalen != 0)
            {
                var tempBuffer = new byte[datalen];
                Array.Copy(buffer, 0, tempBuffer, 0, datalen);
                buffer = tempBuffer;
            }

            return buffer;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="Structure"></typeparam>
        /// <param name="handle"></param>
        /// <param name="code"></param>
        /// <param name="structure"></param>
        internal static void ControlWithOutput<Structure>(
            IntPtr handle, Win32ControlCode code, ref Structure structure)
            where Structure : struct
        {
            uint datalen = 0;
            bool controlResult;
            GCHandle structureHandle;
            IntPtr structurePointer;

            //get our object pointer
            structureHandle = GCHandle.Alloc(structure, GCHandleType.Pinned);
            structurePointer = structureHandle.AddrOfPinnedObject();

            try
            {
                controlResult =
                    Win32Methods.DeviceIoControl(handle, (uint)code,
                    IntPtr.Zero, 0, structurePointer,
                    (uint)Marshal.SizeOf(structure),
                    out datalen, IntPtr.Zero);
            }
            finally
            {
                //always release GH handle
                structureHandle.Free();
            }

            if (!controlResult)
            {
                //copy array over to a correctly sized buffer
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            structure = (Structure)Marshal.PtrToStructure(
                structurePointer, typeof(Structure));
        }
    }
}
